#ifndef QMOSQUITTO_H
#define QMOSQUITTO_H

#include <QObject>
#include <QSocketNotifier>
#include <QTimer>
#include <QMap>
#include <mosquittopp.h>

using namespace mosqpp;

enum {
    QOS_0 = 0,
    QOS_1 = 1,
    QOS_2 = 2
};

class QMosquitto : public QObject, public mosquittopp
{
    Q_OBJECT
public:
    explicit QMosquitto(QObject *parent = 0);
    ~QMosquitto();

    Q_PROPERTY(bool isConnected READ isConnected NOTIFY isConnectedeChanged)
    Q_PROPERTY(QString clientId READ clientId WRITE setClientId NOTIFY clientIdChanged)
    Q_PROPERTY(bool cleanSession READ cleanSession WRITE setCleanSession NOTIFY cleanSessionChanged)
    Q_PROPERTY(QString hostname READ hostname WRITE setHostname NOTIFY hostnameChanged)
    Q_PROPERTY(int port READ port WRITE setPort NOTIFY portChanged)
    Q_PROPERTY(int keepalive READ keepalive WRITE setKeepalive NOTIFY keepaliveChanged)

    Q_PROPERTY(QString username READ username WRITE setUsername NOTIFY usernameChanged)
    Q_PROPERTY(QString password READ password WRITE setPassword NOTIFY passwordChanged)

    Q_PROPERTY(bool tlsEnabled READ tlsEnabled WRITE setTlsEnabled NOTIFY tlsEnabledChanged)

    Q_INVOKABLE int connectToHost();
    Q_INVOKABLE int disconnectFromHost();
    Q_INVOKABLE int reconnectToHost();

    Q_INVOKABLE int subscribe(const QString topic, int qos=0);
    Q_INVOKABLE int unsubscribe(const QString topic);

    Q_INVOKABLE int publish(const QString topic, QString    data, int qos=0, bool retain=false);
    Q_INVOKABLE int publish(const QString topic, QByteArray data, int qos=0, bool retain=false);

    Q_INVOKABLE int setWill(const QString topic, QString    data, int qos=0, bool retain=false);
    Q_INVOKABLE void clearWill();

    Q_ENUMS(mosq_err_t)

    void addTopicMatch(const QString topic, int topic_d);
    int removeTopicMatch(const QString topic);

    void on_connect(int rc) override;
    void on_disconnect(int rc) override;
    void on_message(const struct mosquitto_message *message) override;
    void on_error() override;
    void on_log(int level, const char *str) override;

    void timerEvent(QTimerEvent *event) override;

    bool isConnected() const{return m_isConnected;}
    QString clientId() const{return m_clientId;}
    bool cleanSession() const{return m_cleanSession;}
    QString hostname() const{return m_hostname;}
    int port() const{return m_port;}
    int keepalive() const{return m_keepalive;}
    QString username() const{return m_username;}
    QString password() const{return m_password;}
    bool tlsEnabled() const{return m_tlsEnabled;}

signals:
    void connecting();
    void connected();
    void disconnected();
    void msg(QString topic, QString data);
    void error();
    void topicMatch(int topic_id);

    void isConnectedeChanged(bool connected);
    void clientIdChanged(QString clientId);
    void cleanSessionChanged(bool cleanSession);
    void keepaliveChanged(int keepalive);
    void hostnameChanged(QString hostname);
    void portChanged(int port);
    void usernameChanged(QString username);
    void passwordChanged(QString password);
    void tlsEnabledChanged(bool tlsEnabled);

public slots:
    void setClientId(QString clientId);
    void setCleanSession(bool cleanSession);
    void setKeepalive(int keepalive);
    void setHostname(QString hostname);
    void setPort(int port);
    void setUsername(QString username);
    void setPassword(QString password);
    void setTlsEnabled(bool tlsEnabled);

    void setTls_insecure(bool value)            {m_tls_insecure = value;}
    void setTls_cafile(const QString &value)    {m_tls_cafile = value;}
    void setTls_capath(const QString &value)    {m_tls_capath = value;}
    void setTls_cert(const QString &value)      {m_tls_cert = value;}
    void setTls_key(const QString value)        {m_tls_key = value;}

    QString lastErrorString();

private slots:
    void loopRead();
    void loopWrite();

private:
    void shutdown();
    QString mosq_error_string(int code);

    QSocketNotifier *m_notifier_read;
    QSocketNotifier *m_notifier_write;

    int m_timer;

    // Peer
    QString m_hostname;
    int m_port;
    int m_keepalive;

    // User
    QString m_username;
    QString m_password;

    bool m_cleanSession;
    bool m_isConnected;
    QString m_clientId;

    // TLS
    bool m_tlsEnabled;
    bool m_tls_insecure;
    QString m_tls_cafile;
    QString m_tls_capath;
    QString m_tls_cert;
    QString m_tls_key;

    int m_mid;
    int m_smid;
    int m_pmid;

    QMap<QString, int> m_topics;
    bool connectSocketNotifiers();

    QString m_lastErrorString;
};

#endif // QMOSQUITTO_H
